use std::fmt::{Display, Debug};

fn printer<T: Display>(t: T){
    println!("T:Display type: {}", t);
}

// for function where clause;
fn printer1<T>(t: T) 
where T: Display {
    println!("T:Display type: {}", t);
}

//for type  where clause 
struct S<T: Display>(T);
struct S1<T>(T) where T: Display;

fn base(){
    printer(2);
    printer1(2);

    let _s = S(0_i32);
    let _s = S1(0_i32);
}



trait HasArea {
    fn area(&self) -> f64;
}

#[derive(Debug)]
struct Rectangle { length: f64, height: f64}
impl HasArea for Rectangle {
    fn area(&self) -> f64 { self.length * self.height }
}

//T impl std::fmt::Debug trait
fn print_debug<T: Debug>(t: &T){
    println!("{:?}", t);
}
// T impl trait->HasArea
fn area<T: HasArea>(t: &T) -> f64 { t.area()}

#[allow(dead_code)]
struct Triangle { length: f64, height: f64}

fn custom_struct_impl_bound() {
    let rectangle = Rectangle{ length: 3.0, height: 4.0};
    print_debug(&rectangle);
    println!("Area: {} = {}", rectangle.area(), area(&rectangle));

    let _triangle = Triangle { length: 3.0, height: 4.0};
}



trait Red {}

struct Cardinal;
impl Red for Cardinal {}

fn red<T: Red>(_: &T) -> &'static str { "red" }




trait Blue {}

struct BlueJay;
impl Blue for BlueJay {}

fn blue<T: Blue>(_: &T) -> &'static str { "blue" }


struct Turkey;



fn empty_bounds(){
    let cardinal = Cardinal;
    println!("a cardinal is {}", red(&cardinal));

    let blue_jay = BlueJay;
    println!("A blue jay is {}", blue(&blue_jay));

    let _turkey = Turkey;
}

pub fn main(){
    base();
    custom_struct_impl_bound();

    empty_bounds();
}
